package com.hup.joystick.service.joystick;

import com.hup.joystick.config.AppConfig;
import com.hup.joystick.constant.DeviceKey.KeyActionType;
import com.hup.joystick.constant.DeviceKey.KeyType;
import com.hup.joystick.constant.DeviceKey.NativeKeys;
import com.hup.joystick.model.joystick.NativeEvent;
import com.hup.joystick.model.joystick.NativeEvent.NativeAction;
import com.hup.joystick.model.joystick.NativeKey;
import com.hup.joystick.service.jna.DIJoyState2;
import com.hup.joystick.service.jna.DInputHResultException.HResult;
import com.sun.jna.NativeLong;

import java.util.ArrayList;

/**
 * 将[设备状态]转成[原始按键事件]的处理类
 *
 * @author hugan
 * @date 2020/10/20
 */
public class NativeEventParser {

    /**
     * 当前已按下的按键
     */
    private final ArrayList<NativeAction> lstOldPress = new ArrayList<>();

    public NativeEvent parse(DIJoyState2 state, int hr) {
        if (!HResult.succeeded(hr)) {
            //查询错误,lstOldPress都改成抬起
            NativeEvent res = new NativeEvent(DIJoyState2.EMPTY_STATE, hr);
            for (NativeAction oldPress : lstOldPress) oldPress.actionType = KeyActionType.TYPE_RELEASED;
            res.actions.addAll(lstOldPress);
            lstOldPress.clear();
            return res;
        }

        ArrayList<NativeAction> lstNewPress = stateToActions(state);
        ArrayList<NativeAction> lstAlwaysPress = new ArrayList<>();
        NativeEvent event = new NativeEvent(state, hr);
        //找同时在新旧列表中都按下的按键
        lstNewPress.removeIf(newPress -> {
            for (int i = 0, size = lstOldPress.size(); i < size; i++) {//只删除一个后就return,所以可以正序
                NativeAction oldPress = lstOldPress.get(i);
                if (oldPress.nativeKey.key.equals(newPress.nativeKey.key)) {
                    if (oldPress.nativeKey.type == KeyType.STICK) {
                        //摇杆特殊处理:纳入为[新按下],但数值需要更新
                        oldPress.nativeKey.nativeValue = newPress.nativeKey.nativeValue;
                    }
                    //一直都在按下的
                    lstOldPress.remove(i);//从lstOldPress中移除
                    lstAlwaysPress.add(oldPress);//保留旧事件,弃用新的事件对象
                    return true;//从lstNewPress中移除
                }
                //长按事件思路:按键相同时,如果时间间隔大于n,则不过滤事件
            }
            return false;
        });
        /*
        上面处理完后:
        lstAlwaysPress:一直都按下的
        lstNewPress:新按下的
        lstOldPress:新抬起的
         */
        //复用oldPress对象,仅更新类型
        for (NativeAction oldPress : lstOldPress) oldPress.actionType = KeyActionType.TYPE_RELEASED;

        //重构要发送的按键列表[新按下的,新抬起的]
        event.actions.addAll(lstNewPress);
        event.actions.addAll(lstOldPress);

        //重构当前按下的[一直按下的,新按下的]:这里保证了旧按下的一直在前面
        lstOldPress.clear();
        lstOldPress.addAll(lstAlwaysPress);
        lstOldPress.addAll(lstNewPress);
        //克隆放入到event中
        event.currentPress.addAll(lstOldPress);
        return event;
    }

    public static ArrayList<NativeAction> stateToActions(DIJoyState2 s) {
        ArrayList<NativeAction> res = new ArrayList<>();
        //摇杆轴
        stickToKey(res, s.lX, NativeKeys.X);
        stickToKey(res, s.lY, NativeKeys.Y);
        stickToKey(res, s.lZ, NativeKeys.Z);
        stickToKey(res, s.lRx, NativeKeys.RX);
        stickToKey(res, s.lRy, NativeKeys.RY);
        stickToKey(res, s.lRz, NativeKeys.RZ);
        arrayToKey(res, s.rglSlider, NativeKeys.SLIDER); //未知字段
        stickToKey(res, s.lVX, NativeKeys.VX);
        stickToKey(res, s.lVY, NativeKeys.VY);
        stickToKey(res, s.lVZ, NativeKeys.VZ);
        stickToKey(res, s.lVRx, NativeKeys.VRX);
        stickToKey(res, s.lVRy, NativeKeys.VRY);
        stickToKey(res, s.lVRz, NativeKeys.VRZ);
        arrayToKey(res, s.rglVSlider, NativeKeys.V_SLIDER);
        stickToKey(res, s.lAX, NativeKeys.AX);
        stickToKey(res, s.lAY, NativeKeys.AY);
        stickToKey(res, s.lAZ, NativeKeys.AZ);
        stickToKey(res, s.lARx, NativeKeys.ARX);
        stickToKey(res, s.lARy, NativeKeys.ARY);
        stickToKey(res, s.lARz, NativeKeys.ARZ);
        arrayToKey(res, s.rglASlider, NativeKeys.A_SLIDER);
        stickToKey(res, s.lFX, NativeKeys.FX);
        stickToKey(res, s.lFY, NativeKeys.FY);
        stickToKey(res, s.lFZ, NativeKeys.FZ);
        stickToKey(res, s.lFRx, NativeKeys.FRX);
        stickToKey(res, s.lFRy, NativeKeys.FRY);
        stickToKey(res, s.lFRz, NativeKeys.FRZ);
        arrayToKey(res, s.rglFSlider, NativeKeys.F_SLIDER);

        //方向键
        for (int i = 0; i < s.rgdwPOV.length; i++) {
            if (s.rgdwPOV[i] != NativeKeys.ARROW_DEF_VALUE)
                res.add(new NativeAction(NativeKey.arrow(NativeKeys.getArrowKey(s.rgdwPOV[i], i), s.rgdwPOV[i]), KeyActionType.TYPE_PRESSED));
        }
        //按钮
        for (int i = 0; i < s.rgbButtons.length; i++) {
            if ((s.rgbButtons[i] & 0x80) != 0)
                res.add(new NativeAction(NativeKey.button(NativeKeys.getButtonKey(i), i), KeyActionType.TYPE_PRESSED));
        }
        return res;
    }

    protected static void stickToKey(ArrayList<NativeAction> res, NativeLong v, String[] nativeKey) {
        handleIgnoreValue(v);
        long value = v.longValue();
        if (value != 0) {
            res.add(new NativeAction(NativeKey.stick(nativeKey[value < 0 ? 0 : 1], value), KeyActionType.TYPE_PRESSED));
        }
    }

    /**
     * 处理摇杆硬件或其他原因导致某个值一直不为零的情况
     */
    private static void handleIgnoreValue(NativeLong v) {
        long value = v.longValue();
        if (value == 0) return;
        if (value >= -AppConfig.STICK_IGNORE_VALUE && value <= AppConfig.STICK_IGNORE_VALUE) {
            v.setValue(0);
        }
    }

    /**
     * @apiNote 无设备验证此方法是否准确!
     */
    protected static void arrayToKey(ArrayList<NativeAction> res, NativeLong[] array, String[] nativeKey) {
        for (int i = 0; i < 2; i++) {
            if (array[i] != null && array[i].longValue() != 0) {
                res.add(new NativeAction(NativeKey.stick(nativeKey[i], array[i].longValue()), KeyActionType.TYPE_PRESSED));
            }
        }
    }

}
